﻿using MongoDB.Driver;
using Sustainalytics.Entities.RiskRatings;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ScreeningTool.Synchronization.Handlers.RiskRatings
{
    public class MaterialEsgIssueHandler : IHandlerWithGrouping<RiskRatingMaterialEsgIssue>
    {
        private readonly IMongoCollection<EventIndicator> _eventIndicatorCollection;

        public MaterialEsgIssueHandler(IMongoCollection<EventIndicator> eventIndicatorCollection)
        {
            _eventIndicatorCollection = eventIndicatorCollection;
        }

        public ProjectionDefinition<RiskRatingMaterialEsgIssue> GetProjection(ProjectionDefinitionBuilder<RiskRatingMaterialEsgIssue> builder) => builder
            .Include(x => x.CompanyId)
            .Include(x => x.SortKey)
            .Include(x => x.Indicators)
            .Include(x => x.Name)
            .Include(x => x.Type)
            .Include(x => x.TypeTimestamp)
            .Include(x => x.MeiCode)
            .Include(x => x.IsMain)
            .Include(x => x.Assessment)
            .Include(x => x.ManagementCluster)
            .Include(x => x.ExposureCluster)
            .Include(x => x.FinalWeight)
            .Include(x => x.ManageableRiskFactorPercentile)
            .Include(x => x.SubIndustryExposureScore)
            .Include(x => x.ExcessExposure)
            .Include(x => x.FinalBetaFactor)
            .Include(x => x.ManagementRisk)
            .Include(x => x.Narrative)
            .Include(x => x.ExposureComment)
            .Include(x => x.ManagementComment)
            .Include(x => x.AnalystViewComment)
            .Include(x => x.AnalystViewExposureAssessment)
            .Include(x => x.AnalystViewManagementAssessment)
            .Include(x => x.AnalystViewConsolidated);

        public IEnumerable<ExtraElement> GetExtraElements(IEnumerable<RiskRatingMaterialEsgIssue> entities)
        {
            var eventIndicators = GetEventIndicators(entities);

            return EnumerableExtensions.Concat(
                entities.GetExtraElements(eventIndicators),
                entities.GetIdiosyncraticExtraElements(),
                entities.GetPillarsExtraElements(),
                entities.GetNotableIssuesExtraElements(),
                eventIndicators.GetExtraElements());
        }

        private IEnumerable<EventIndicator> GetEventIndicators(IEnumerable<RiskRatingMaterialEsgIssue> entities)
        {
            var companyId = entities.First().CompanyId;

            var eventCodes = entities
                .SelectMany(entity => entity.Indicators)
                .Where(x => x.IndicatorType == IndicatorType.EventIndicator)
                .Select(x => x.Code)
                .ToList();

            var eventIndicatorProjection = Builders<EventIndicator>.Projection
                .Include(x => x.CompanyId)
                .Include(x => x.CanvasCode)
                .Include(x => x.Name)
                .Include(x => x.EventJustification)
                .Include(x => x.OutlookComments)
                .Include(x => x.Outlook)
                .Include(x => x.OutlookText)
                .Include(x => x.ControversySummary)
                .Include(x => x.EventCategoryText);

            return _eventIndicatorCollection
                .Find(x => x.CompanyId == companyId && eventCodes.Contains(x.CanvasCode))
                .Project<EventIndicator>(eventIndicatorProjection)
                .ToList();
        }
    }

    public static class RiskRatingMaterialEsgIssuesExtensions
    {
        public static IEnumerable<ExtraElement> GetIdiosyncraticExtraElements(this IEnumerable<RiskRatingMaterialEsgIssue> entities)
        {
            return entities
                .Where(x => x.Type == MaterialIssueType.IdiosyncraticIssue)
                .SelectMany((entity, index) =>
                {
                    var key = $"Idiosyncratic Issue {index + 1}";
                    return new ExtraElementList
                    {
                        { $"{key}-Event Name",         entity.Name                         },
                        { $"{key}-Event Category",     entity.Assessment                   },
                        { $"{key}-Risk Score",         entity.ManagementRisk.UnmanagedRisk },
                        { $"{key}-Risk Contribution",  entity.FinalWeight                  },
                        { $"{key}-Exposure Score",     entity.ManagementRisk.Exposure      },
                        { $"{key}-Managed Risk Score", entity.ManagementRisk.ManagedRisk   },
                    };
                });
        }

        public static IEnumerable<ExtraElement> GetExtraElements(
            this IEnumerable<RiskRatingMaterialEsgIssue> entities,
            IEnumerable<EventIndicator> eventIndicators) => entities.SelectMany(entity =>
        {
            var name = entity.Type == MaterialIssueType.BaselineIssue
                ? "Corporate Governance"
                : entity.Name.Replace(" - SC", " - Supply Chain");

            var key = $"Issue - {name}";
            var avKey = $"{key}-Analyst View";

            return new ExtraElementList
            {
                { $"{key}-Event Name",              entity.Name                            },
                { $"{key}-Type",                    entity.Type, entity.TypeTimestamp      },
                { $"{key}-Code",                    entity.MeiCode                         },
                { $"{key}-Notable Issue Flag",      entity.IsMain                          },
                { $"{key}-Risk Category",           entity.Assessment                      },
                { $"{key}-Management Category",     entity.ManagementCluster               },
                { $"{key}-Exposure Category",       entity.ExposureCluster                 },
                { $"{key}-Risk Contribution",       entity.FinalWeight                     },
                { $"{key}-Manageable Risk Factor",  entity.ManageableRiskFactorPercentile  },
                { $"{key}-Default Exposure Score",  entity.SubIndustryExposureScore        },
                { $"{key}-Excess Exposure Score",   entity.ExcessExposure                  },
                { $"{key}-Beta",                    entity.FinalBetaFactor                 },
                { $"{key}",                         entity.GetGroup(eventIndicators)       },

                { $"{key}-Risk Score",              entity.ManagementRisk.UnmanagedRisk    },
                { $"{key}-Exposure Score",          entity.ManagementRisk.Exposure         },
                { $"{key}-Management Score",        entity.ManagementRisk.Management       },
                { $"{key}-Manageable Risk Score",   entity.ManagementRisk.ManageableRisk   },
                { $"{key}-Unmanageable Risk Score", entity.ManagementRisk.UnmanageableRisk },
                { $"{key}-Managed Risk Score",      entity.ManagementRisk.ManagedRisk      },
                { $"{key}-Management Gap Score",    entity.ManagementRisk.ManagementGap    },

                { $"{avKey} Subindustry Exposure Narrative", entity.Narrative                       },
                { $"{avKey} Exposure Narrative",             entity.ExposureComment                 },
                { $"{avKey} Management Narrative",           entity.ManagementComment               },
                { $"{avKey} Assessment",                     entity.AnalystViewComment              },
                { $"{avKey} Exposure Assessment",            entity.AnalystViewExposureAssessment   },
                { $"{avKey} Management Assessment",          entity.AnalystViewManagementAssessment },
                { $"{avKey} Consolidated",                   entity.AnalystViewConsolidated         },
            };
        });

        public static IEnumerable<ExtraElement> GetPillarsExtraElements(this IEnumerable<RiskRatingMaterialEsgIssue> entities)
        {
            var baselineIssues = entities.Where(x => x.Type == MaterialIssueType.BaselineIssue);
            var baselineIssuesCount = baselineIssues.Count();

            if (baselineIssuesCount != 1)
            {
                var companyId = entities.First().CompanyId;
                Log.Warning($"Company with id {companyId} has {baselineIssuesCount} baseline issues!");
                return new ExtraElementList();
            }

            return baselineIssues.Single()?.Indicators.SelectMany(GetPillarExtraElements);
        }

        public static IEnumerable<ExtraElement> GetPillarExtraElements(this RiskRatingIndicatorBase entity)
        {
            var key = entity.Name;
            return new ExtraElementList
            {
                { $"{key}-Raw Score-RR",                   entity.RawScore      },
                { $"{key}-Weight-Consolidated-RR",         entity.FinalWeight   },
                { $"{key}-Weighted Score-Consolidated-RR", entity.WeightedScore },
            };
        }

        public static IssueGroup GetGroup(
            this RiskRatingMaterialEsgIssue issue,
            IEnumerable<EventIndicator> eventIndicators)
        {
            var extraElements = issue.Indicators.SelectMany(indicator =>
            {
                var key = indicator.GetIndicatorKey(issue.Type);
                var evIndicator = eventIndicators.SingleOrDefault(x => x.CompanyId == issue.CompanyId && x.CanvasCode == indicator.Code);

                return new ExtraElementList
                {
                    { $"{key}-Weight-Overall-RR",         indicator.FinalWeight          },
                    { $"{key}-Weighted Score-Overall-RR", indicator.WeightedScore        },
                    { $"{key}-Weight-Issue-RR",           indicator.WeightInMei          },
                    { $"{key}-Weighted Score-Issue-RR",   indicator.WeightedScoreInMei   },

                    { $"{key}-Answer Category-RR",        indicator.GetAnswerCategory()  },
                    { $"{key}-Raw Score-RR",              indicator.RawScore             },
                    { $"{key}-Category-RR",               evIndicator?.EventCategoryText },
                    { $"{key}-Outlook Score-RR",          evIndicator?.Outlook           },
                };
            }).AsGroupExtraElements();

            return new IssueGroup { Code = issue.MeiCode, ExtraElements = extraElements };
        }

        public static IEnumerable<ExtraElement> GetNotableIssuesExtraElements(this IEnumerable<RiskRatingMaterialEsgIssue> entities) => entities
            .Where(x => x.IsMain && x.SortKey.Value > 0)
            .OrderBy(x => x.SortKey.Value)
            .Take(3)
            .SelectMany((entity, index) =>
            {
                var key = $"Notable Issue {index + 1}";
                return new ExtraElementList
                {
                    { $"{key}-Id",                        entity.Id                      },
                    { $"{key}-Name",                      entity.Name                    },
                    { $"{key}-Analyst View Consolidated", entity.AnalystViewConsolidated },
                };
            });

        public static IEnumerable<ExtraElement> GetExtraElements(this IEnumerable<EventIndicator> eventIndicators)
            => eventIndicators.SelectMany(x => new ExtraElementList
            {
                { $"{x.Name}-Answer Category Justification-RR", x.EventJustification                },
                { $"{x.Name}-Outlook Comments-RR",              x.OutlookComments.JoinStrings("  ") },
                { $"{x.Name}-Outlook Score-RR",                 x.Outlook                           },
                { $"{x.Name}-Outlook Category-RR",              x.OutlookText                       },
                { $"{x.Name}-Summary-RR",                       x.ControversySummary                },
                { $"{x.Name}-Category-RR",                      x.EventCategoryText                 },
            });
    }

    public class IssueGroup
    {
        public string Code { get; set; }
        public Dictionary<string, object> ExtraElements { get; set; }
    }
}
